﻿/*******************************************************
 * 
 * 作者：胡庆访
 * 创建时间：20100330
 * 说明：对象实体，所有对象实体的基类
 * 运行环境：.NET 3.5 SP1
 * 版本号：1.0.0
 * 
 * 历史记录：
 * 创建文件 胡庆访 20100330
 * 添加延迟外键 胡庆访 20100401
 * 自动获取属性，用于支持属性的复制 胡庆访 20100402
 * 
*******************************************************/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Csla;
using Csla.Core;
using System.ComponentModel;
using lite;
using System.Reflection;
using System.Diagnostics;
using lite.sqlserver;
using System.Data;
using OpenExpressApp.DB.Utils;
using OpenExpressApp.MetaModel;

namespace OpenExpressApp
{
    /// <summary>
    /// 对象实体
    /// 
    /// 继承此类，可以获得以下功能：
    /// *去除CSLA的属性权限验证，提高速度。
    /// *自动管理CSLA属性，子类可以通过属性AllProperties获取到所有的CSLA属性。
    /// *实现IClonable(T)接口，并自动复制对应的CSLA属性。
    /// *默认实现了所有数据库访问的方法：CDUQ。
    /// *提供了延迟加载子对象集合的方法。
    /// *提供了创建延迟外键对象的方法。
    /// *子类如果声明了Name属性，则ToString方法会显示Name属性的值。
    /// </summary>
    /// <typeparam name="T"></typeparam>
    [Serializable]
    public abstract class Entity<T> : Csla.GBusinessBase<T>, IEntity, IReferenceOwner, ICloneable<T>
        where T : Entity<T>
    {
        #region 属性

        /// <summary>
        /// 主键
        /// </summary>
        private static PropertyInfo<Guid> IdProperty =
            RegisterProperty(new PropertyInfo<Guid>("Id"));
        [DataObjectField(true, true)]
        [Column, PK]
        public virtual Guid Id
        {
            get { return GetProperty(IdProperty); }
            set { SetProperty(IdProperty, value); }
        }

        #endregion

        #region 值的复制

        /// <summary>
        /// 把旧对象的所有属性都读取到本对象中。
        /// 读取完成后，旧对象不再可用。（孩子集合属性中的所有孩子的父指针已经被更改。）
        /// </summary>
        /// <param name="oldObject"></param>
        /// <returns></returns>
        public override BusinessBase MergeOldObject(BusinessBase oldObject)
        {
            var entity = oldObject as T;

            this.MergeOldEntity(entity);

            return base.MergeOldObject(oldObject);
        }

        /// <summary>
        /// 从数据库对象中读取所有的属性，并设置好孩子结点的父指针。
        /// </summary>
        /// <param name="oldEntity"></param>
        public void MergeOldEntity(T oldEntity)
        {
            this.CopyValueFrom(oldEntity, true, true);
        }

        /// <summary>
        /// 复制目标对象的所有字段。
        /// </summary>
        /// <param name="target"></param>
        public void Clone(T target)
        {
            this.Clone(target, true);
        }

        /// <summary>
        /// 复制目标对象的所有字段。
        /// </summary>
        /// <param name="target"></param>
        /// <param name="cloneChildren">是否复制孩子对象的引用</param>
        public virtual void Clone(T target, bool cloneChildren)
        {
            this.CopyValueFrom(target, cloneChildren, false);
        }

        /// <summary>
        /// 复制目标对象的所有字段。
        /// </summary>
        /// <param name="target"></param>
        /// <param name="cloneChildren">是否复制孩子对象的引用</param>
        /// <param name="resetParent">
        /// 如果需要复制孩子对象，则这个参数表示是否在复制完成后，
        /// 调用ResetParent方法对所有孩子都重设父指针。
        /// </param>
        private void CopyValueFrom(T target, bool cloneChildren, bool resetParent)
        {
            foreach (var property in AllProperties)
            {
                var propertyInfo = property.PropertyInfo;

                if (target.FieldExists(propertyInfo.Name))
                {
                    if (property.IsChild)
                    {
                        if (cloneChildren)
                        {
                            this.LoadProperty(propertyInfo, target.ReadProperty(propertyInfo));
                            if (resetParent)
                            {
                                this.ResetParent(propertyInfo);
                            }
                        }
                    }
                    else
                    {
                        this.LoadProperty(propertyInfo, target.ReadProperty(propertyInfo));
                    }
                }
            }
        }

        /// <summary>
        /// 设置所有孩子对象的父指针为当前对象。
        /// for example
        /// if (propertyInfo == ChildrenProperty)
        /// {
        ///     foreach (var child in this.Children)
        ///     {
        ///         child.Parent = this;
        ///     }
        /// }
        /// else if(propertyInfo == Children2Property)
        /// ...
        /// </summary>
        /// <param name="propertyInfo"></param>
        protected virtual void ResetParent(Csla.Core.IPropertyInfo propertyInfo)
        {
            //不使用abstract方法，而是使用冗余实现方式。
            throw new NotSupportedException("子类必须重写ResetParent方法，然后设置所有孩子对象的父指针为当前对象。");
            /*
            string s = @"
            if (propertyInfo == ProjectPBSsProperty)
            {
                foreach (var child in this.ProjectPBSs)
                {
                    child.Project = this;
                }
            }
            else if (propertyInfo == ProjectContractSubjectsProperty)
            {
                foreach (var child in this.ProjectContractSubjects)
                {
                    child.Project = this;
                }
            }
            else
            {
                base.ResetParent(propertyInfo);
            }
            ";
            */
        }
             
        /// <summary>
        /// 复制某个属性
        /// </summary>
        /// <param name="target">从这个对象拷贝</param>
        /// <param name="property">拷贝这个属性</param>
        protected void CopyProperty(T target, Csla.Core.IPropertyInfo property)
        {
            //if (target.FieldManager.FieldExists(property))
            {
                this.LoadProperty(property, target.ReadProperty(property));
            }
        }

        #region 获取所有的IPropertyInfo

        private static object _asyncRoot = new object();

        private static List<CSLAPropertyInfo> _allProperties;

        private static IList<CSLAPropertyInfo> AllProperties
        {
            get
            {
                InitAllStaticProperties();
                return _allProperties.AsReadOnly();
            }
        }

        /// <summary>
        /// 获取所有的IPropertyInfo
        /// </summary>
        private static void InitAllStaticProperties()
        {
            if (_allProperties == null)
            {
                lock (_asyncRoot)
                {
                    if (_allProperties == null)
                    {
                        var result = new List<CSLAPropertyInfo>();

                        var type = typeof(T);
                        while (true)
                        {
                            result.AddRange(LoadFromType(type));
                            if (type == typeof(Entity<T>) || type == typeof(GBusinessBase<T>)) break;
                            type = type.BaseType;
                        }

                        _allProperties = result;
                    }
                }
            }
        }

        private static IEnumerable<CSLAPropertyInfo> LoadFromType(Type type)
        {
            var staticFields = type.GetFields(BindingFlags.Static | BindingFlags.NonPublic | BindingFlags.Public);
            return staticFields.Where(f => typeof(Csla.Core.IPropertyInfo).IsAssignableFrom(f.FieldType))
                .Select(f =>
                {
                    bool isChild = false;
                    var property = f.GetValue(null) as Csla.Core.IPropertyInfo;

                    //判断是不是孩子对象
                    var fieldType = f.FieldType;
                    if (fieldType.IsGenericType && fieldType.GetGenericTypeDefinition() == typeof(PropertyInfo<>))
                    {
                        var argType = fieldType.GetGenericArguments()[0];

                        while (argType.BaseType != null)
                        {
                            if (argType.IsGenericType && argType.GetGenericTypeDefinition() == typeof(GBusinessListBase<,>))
                            {
                                isChild = true;
                                break;
                            }
                            argType = argType.BaseType;
                        }
                    }

                    return new CSLAPropertyInfo(property, isChild);
                });
        }

        private class CSLAPropertyInfo
        {
            private Csla.Core.IPropertyInfo _propertyInfo;

            private bool _isChild;

            public CSLAPropertyInfo(Csla.Core.IPropertyInfo propertyInfo, bool isChild)
            {
                Debug.Assert(propertyInfo != null, "propertyInfo 为空!");

                this._propertyInfo = propertyInfo;
                this._isChild = isChild;
            }

            public Csla.Core.IPropertyInfo PropertyInfo
            {
                get
                {
                    return this._propertyInfo;
                }
            }

            public bool IsChild
            {
                get
                {
                    return this._isChild;
                }
            }

            public override string ToString()
            {
                return this._propertyInfo.Name;
            }
        }

        #endregion

        #endregion

        #region 根对象

        [RunLocal()]
        protected override void DataPortal_Create()
        {
            this.OnCreate();

            base.DataPortal_Create();
        }

        [Transactional(TransactionalTypes.TransactionScope)]
        protected override void DataPortal_Insert()
        {
            this.OnInsert();
        }

        [Transactional(TransactionalTypes.TransactionScope)]
        protected override void DataPortal_Update()
        {
            this.OnUpdate();
        }

        [Transactional(TransactionalTypes.TransactionScope)]
        protected override void DataPortal_DeleteSelf()
        {
            this.OnDelete();
        }

        [Transactional(TransactionalTypes.TransactionScope)]
        protected override void DataPortal_Delete(object criteria)
        {
            this.OnDelete();
        }

        public static T New()
        {
            return DataPortal.Create<T>();
        }

        #endregion

        #region 子对象

        [RunLocal()]
        protected override void Child_Create()
        {
            this.OnCreate();

            base.Child_Create();
        }

        /// <summary>
        /// 空方法，用于支持GetChild静态方法。
        /// </summary>
        protected virtual void Child_Fetch() { }

        protected virtual void Child_Fetch(T data)
        {
            this.Clone(data, false);
        }

        protected virtual void Child_Insert(BusinessBase parent)
        {
            this.OnInsert();
        }

        protected virtual void Child_Update(BusinessBase parent)
        {
            this.OnUpdate();
        }

        protected virtual void Child_DeleteSelf(BusinessBase parent)
        {
            this.OnDelete();
        }

        /// <summary>
        /// 创建一个新的对象
        /// </summary>
        /// <returns></returns>
        public static T NewChild()
        {
            return DataPortal.CreateChild<T>();
        }

        /// <summary>
        /// 调用此方法返回孩子对象。
        /// 返回的子对象的属性只是简单的完全Copy参数data的数据。
        /// </summary>
        /// <param name="data">这个对象只有数据，没有CSLA相关的属性。如：IsChild。</param>
        /// <returns></returns>
        public static T GetChild(T data)
        {
            var result = DataPortal.FetchChild<T>(data);
            return result;
        }

        /// <summary>
        /// 获取一个空属性的子对象对象
        /// </summary>
        /// <returns></returns>
        public static T GetChild()
        {
            var result = DataPortal.FetchChild<T>();
            return result;
        }

        #endregion

        #region 数据层实现

        /// <summary>
        /// 重写这个方法，添加代码为非CSLA属性赋初始值。
        /// </summary>
        protected virtual void OnCreate()
        {
            this.LoadProperty(IdProperty, Guid.NewGuid());

            for (int i = 0, c = AllProperties.Count; i < c; i++)
            {
                var propInfo = AllProperties[i];
                //孩子集合属性，不需要加载默认属性。
                if (propInfo.IsChild == false)
                {
                    var property = propInfo.PropertyInfo;
                    if (property != IdProperty)
                    {
                        object value = property.DefaultValue;

                        //处理DateTime类型的默认值为当前时间。
                        if (property.Type == typeof(DateTime) &&
                            default(DateTime).Equals(value)
                            )
                        {
                            value = DateTime.Now;
                        }

                        this.LoadProperty(property, value);
                    }
                }
            }
        }

        protected virtual void OnInsert()
        {
            using (var db = this.CreateDb())
            {
                db.Insert(this);

                // update child objects
                this.FieldManager.UpdateChildren(this);
            }
        }

        protected virtual void OnUpdate()
        {
            using (var db = this.CreateDb())
            {
                db.Update(this);

                // update child objects
                this.FieldManager.UpdateChildren(this);
            }
        }

        protected virtual void OnDelete()
        {
            using (var db = this.CreateDb())
            {
                db.Delete(this);
            }
        }

        #endregion

        #region 延迟加载

        /// <summary>
        /// 创建一个使用延迟加载的外键的引用
        /// </summary>
        /// <typeparam name="TRefEntity"></typeparam>
        /// <param name="loader"></param>
        /// <returns></returns>
        protected ILazyEntityRef<TRefEntity> CreateReference<TRefEntity>(Func<Guid, TRefEntity> loader)
            where TRefEntity : Entity<TRefEntity>
        {
            return new LazyEntityRef<TRefEntity>(loader, this, true);
        }

        /// <summary>
        /// 创建一个使用延迟加载的外键的引用
        /// </summary>
        /// <typeparam name="TRefEntity"></typeparam>
        /// <param name="loader"></param>
        /// <returns></returns>
        protected ILazyEntityRef<TRefEntity> CreateReference<TRefEntity>(Func<Guid, TRefEntity> loader, bool notifyRefEntityChanged)
            where TRefEntity : Entity<TRefEntity>
        {
            return new LazyEntityRef<TRefEntity>(loader, this, notifyRefEntityChanged);
        }

        /// <summary>
        /// 延迟加载子对象的集合。
        /// </summary>
        /// <typeparam name="TCollection">
        /// 子对象集合类型
        /// </typeparam>
        /// <param name="propertyInfo">当前属性的元信息</param>
        /// <param name="creator">构造一个新的子集合</param>
        /// <param name="childrenLoader">根据当前对象，查询出一个子集合</param>
        /// <returns></returns>
        protected TCollection GetLazyChildren<TCollection>(PropertyInfo<TCollection> propertyInfo, Func<TCollection> creator, Func<T, TCollection> childrenLoader)
        {
            if (this.FieldManager.FieldExists(propertyInfo) == false)
            {
                if (this.IsNew)
                {
                    this.LoadProperty(propertyInfo, creator());
                }
                else
                {
                    this.LoadProperty(propertyInfo, childrenLoader(this as T));
                }
            }
            return this.GetProperty(propertyInfo);
        }

        #region IReferenceOwner Members

        /// <summary>
        /// “私有”实现此接口
        /// 外键Id改变时，需要设置Dirty、引发事件 等。
        /// </summary>
        /// <param name="refType"></param>
        void IReferenceOwner.NotifyIdChanged(Type refType)
        {
            this.PropertyHasChanged(refType.Name + DBConvention.FieldName_Id);
        }

        /// <summary>
        /// “私有”实现此接口
        /// 外键实体改变时，只需要引发事件即可。
        /// </summary>
        /// <param name="refType"></param>
        void IReferenceOwner.NotifyEntityChanged(Type refType)
        {
            this.OnPropertyChanged(refType.Name);
        }

        #endregion

        #endregion

        #region 重写Object的方法

        public override string ToString()
        {
            //尝试读取Name属性。
            var nameProperty = AllProperties.FirstOrDefault(p => p.PropertyInfo.Name == "Name");
            if (nameProperty != null)
            {
                var value = this.GetProperty(nameProperty.PropertyInfo);
                if (value != null)
                {
                    return value.ToString();
                }
            }

            return typeof(T).Name + " " + this.Id;
        }

        //获取Id属性太慢，去除以下属性。

        //public override int GetHashCode()
        //{
        //    return this.Id.GetHashCode();
        //}

        //public override bool Equals(object obj)
        //{
        //    var target = obj as T;

        //    if (target != null)
        //    {
        //        return this.Id.Equals(target.Id);
        //    }

        //    return base.Equals(obj);
        //}

        #endregion

        /// <summary>
        /// 不需要检测权限，直接返回true
        /// </summary>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public override bool CanReadProperty(string propertyName)
        {
            return true;
        }

        /// <summary>
        /// 不需要检测权限，直接返回true
        /// </summary>
        /// <param name="propertyName"></param>
        /// <returns></returns>
        public override bool CanWriteProperty(string propertyName)
        {
            return true;
        }

        protected virtual IDb CreateDb()
        {
            return DBHelper.CreateDb(ConnectionStringNames.OpenExpressApp);
        }

        #region IEntity Members

        void IEntity.MergeOldObject(IEntity entity)
        {
            this.MergeOldObject(entity as BusinessBase);
        }

        #endregion
    }

    #region ChildCriteria

    /// <summary>
    /// 加载子对象时，使用这个参数
    /// </summary>
    [Serializable]
    public class ChildCriteria : CriteriaBase
    {
        public ChildCriteria(bool loadAsChildren)
        {
            this.LoadAsChildren = loadAsChildren;
        }

        /// <summary>
        /// 是否把数据加载为根对象
        /// </summary>
        public bool LoadAsChildren { get; private set; }
    }

    /// <summary>
    /// 有一个Guid的查询条件
    /// </summary>
    [Serializable]
    public class GuidChildCriteria : ChildCriteria
    {
        public GuidChildCriteria(Guid value) : this(value, true) { }

        public GuidChildCriteria(Guid value, bool loadAsChildren)
            : base(loadAsChildren)
        {
            this.Value = value;
        }

        /// <summary>
        /// 值
        /// </summary>
        public Guid Value { get; private set; }
    }

    #endregion
}